(function(global, factory) {
  typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
    typeof define === 'function' && define.amd ? define([], factory) :
    global.t = factory();
}(this, function() {

  var ST = function(v) {
    // The jQuery object is actually just the init constructor 'enhanced'
    return new ST.fn.init(v);
  };

  ST.fn = ST.prototype = {
    constructor: ST,
    init: function(v) {
      this._v = v;
    }
  };

  ST.fn.init.prototype = ST.fn;

  ST.extend = ST.fn.extend = function() {
    var src, copy, dest, i, options,
      deep = false,
      copyIsArray = false,
      length = arguments.length;

    if (typeof arguments[0] === "boolean") {
      deep = true;
      dest = arguments[1] || {};
      i = 2;
    } else {
      dest = arguments[0] || {};
      i = 1;
    }

    if (length === i) {
      dest = this;
      i--;
    }

    for (; i < length; i++) {
      if ((options = arguments[i]) != null) {
        for (name in options) {
          src = dest[name];
          copy = options[name];

          if (deep && (ST.isObject(copy) || (copyIsArray = ST.isArray(copy)))) {
            if (copyIsArray) {
              src = ST.isArray(src) ? src : [];
            } else {
              src = ST.isObject(src) ? src : {};
            }

            dest[name] = ST.extend(deep, src, copy);
          } else {
            dest[name] = copy;
          }
        }
      }
    }
    return dest;
  }

  ST.extend({
    isFunction: function(val) {
      return Object.prototype.toString.call(val) == "[object Function]";
    },
    isObject: function(val) {
      return Object.prototype.toString.call(val) == "[object Object]";
    },
    isRegExp: function(val) {
      return Object.prototype.toString.call(val) == "[object RegExp]";
    },
    isArray: Array.isArray || function(val) {
      return Object.prototype.toString.call(val) == "[object Array]";
    },
    param: function(o) {
      var queryString = [];
      for (var param in o) {
        queryString.push(param + '=' + encodeURIComponent(o[param]));
      }
      return queryString.join('&').replace('/%20/g', '+');
    },
    toProperCase: toProperCase,
    reverse: reverse,
    match: myMatch,
    matchBetween: matchBetween,
    matchAll: matchAll
  });

  ST.fn.extend({
    toProperCase: function() {
      return toProperCase(this._v);
    },
    reverse: function() {
      return reverse(this._v);
    }
  });

  function toProperCase(str) {
    return str.substring(0, 1).toUpperCase() + str.substring(1);
  }

  function reverse(str) {
    return str.split("").reverse().join("");
  }

  /**
   * [myMatch 支持正则表达式匹配]
   * @param  {[type]} str     [字符串]
   * @param  {[type]} search  [要查找的字符串或正则表达式]
   * @param  {[type]} offset [要查找起始位置]
   * @return {[type]}         [{pos: '匹配的起始位置', match: '匹配的字符串'}]
   */
  function myMatch(str, search, offset) {
    offset = offset || 0;
    if (typeof search === 'string') {
      var pos = str.indexOf(search, offset);
      if (pos === -1)
        return -1;
      else
        return {
          pos: pos,
          match: search
        };
    } else if (search instanceof RegExp) {
      if (!search.global) { //没有g应该就不用加g了
        search = new RegExp(search.source, 'g' + (search.ignoreCase ? 'i' : '') + (search.multiline ? 'm' : ''));
      }
      var match = search.exec(str.substring(offset));
      if (match !== null) {
        return {
          //lastIndex是尾巴的位置
          pos: search.lastIndex - match[0].length + offset,
          match: match[0]
        }
      } else {
        return -1;
      }
    }
  }

  function matchBetween(str, start, end, offset) {
    var ofrontMatched;
    var obackMatched;

    if ((ofrontMatched = myMatch(str, start, offset)) == -1) {
      return -1;
    } else {
      var middlePos = ofrontMatched.pos + ofrontMatched.match.length;
      if ((obackMatched = myMatch(str, end, middlePos)) == -1) {
        return -1;
      } else {
        var middleMatch = str.substring(middlePos, obackMatched.pos);
        return {
          startPos: ofrontMatched.pos,
          startMatch: ofrontMatched.match,
          endPos: obackMatched.pos,
          endMatch: obackMatched.match,
          middlePos: middlePos,
          middleMatch: middleMatch
        }
      }
    }
  }

  function matchAll(str, search, cb, offset) {
    offset = offset || 0;
    if (!search.global) {
      search = new RegExp(search.source, 'g');
    }
    var arr = [];
    var match;
    var tmp = str.substring(offset);
    while ((match = search.exec(tmp)) != null) {
      var matchData = {
        pos: search.lastIndex - match[0].length + offset,
        match: match
      }
      if (cb)
        cb(matchData);
      arr.push(matchData);
    }

    return arr;
  }

  function splitBy(str, searchStr, cb) {
    var arr = [],
      startIdx = 0,
      endIdx = 0,
      piece = "";

    while (startIdx !== -1) {
      startIdx = str.indexOf(searchStr, endIdx);
      if (startIdx < 0) {
        piece = str.slice(endIdx);
      } else {
        piece = str.slice(endIdx, startIdx);
        endIdx = startIdx + searchStr.length;
      }
      if (cb)
        cb(piece);
      arr.push(piece);
    }
    return arr;
  }

  function splitBetween(str, start, end, cb, cb2) {
    var ifrontPos = 0;
    var ibackPos = 0;

    if ((ifrontPos = myMatch(str, start)) == -1) {
      cb(str);
      if (cb2)
        cb2();
    } else {
      if ((ibackPos = myMatch(str, end, ifrontPos.pos + ifrontPos.match.length)) == -1) {
        cb(str);
        if (cb2)
          cb2();
      } else {
        var a = str.substr(0, ifrontPos.pos);
        var b = str.substr(ifrontPos.pos + ifrontPos.match.length, ibackPos.pos - ifrontPos.pos - ifrontPos.match.length);
        var pos = 0;
        if ((pos = myMatch(b, start)) != -1) {
          cb(str.substr(0, pos.pos + pos.match.length));
          splitBetween(str.substr(pos.pos + pos.match.length), start, end, cb, cb2);
        } else {
          cb(a, b);
          splitBetween(str.substr(ibackPos.pos + ibackPos.match.length), start, end, cb, cb2);
        }
      }
    }
  }

  ST.split = function() {
    if (arguments.length > 3)
      return splitBetween.apply(this, arguments);
    else
      return splitBy.apply(this, arguments);
  };

  function function2string(k, v) {
    if (typeof v === 'function') {
      return v + '';
    }
    return v;
  }

  function string2function(k, v) {
    if (typeof v === 'string' && v.indexOf('function') > -1) {
      return eval('(function(){return ' + v + ' })()');
    }
    return v;
  }

  /**
   * 序列化json对象或js对象
   * @param  {[type]} obj [description]
   * @return {[type]}     [description]
   */
  function serialize(obj) {
    /**
     * JSON.stringify(value[, replacer[, space]])
     */
    return JSON.stringify(obj, function2string);
  }

  /**
   * 反序列化json字符串
   * @param  {[type]} obj [description]
   * @return {[type]}     [description]
   */
  function unSerialize(obj) {
    return JSON.parse(obj, string2function);
  }

  /**
   * 深拷贝，支持 值为 function 类型
   * @param  {[type]} obj [description]
   * @return {[type]}     [description]
   */
  function deepCopy(obj) {
    return unSerialize(serialize(obj));
  }

  ST.serialize = serialize;
  ST.unSerialize = unSerialize;
  ST.deepCopy = deepCopy;

  /**
   * 序列化表单js对象
   * @param  {[Object]} obj [description]
   * @return {[String]}     [description]
   */
  function param(obj) {}
  /**
   * 反序列化表单multiQueryString
   * @param  {[String]} string [description]
   * @return {[Obj]}        [description]
   */
  function deparam(string) {}

  function genObject(aK, aV) {
    var obj = {}
    for (var i = 0; i < aK.length; i++) {
      obj[aK[i]] = aV[i];
    }
    return obj;
  }

  ST.genObject = genObject;

  function inherits(clazz, baseClazz) {
    var clazzPrototype = clazz.prototype;
    function F() {}
    F.prototype = baseClazz.prototype;
    clazz.prototype = new F();
    // clazz.prototype = baseClazz.prototype;

    for (var param in clazzPrototype) {
      clazz.prototype[param] = clazzPrototype[param];
    }

    clazz.prototype.constructor = clazz;
    clazz.superClass = baseClazz
  }

  ST.inherits = inherits;

  /**
   * [recursive recursive + call + callback]
   * @param  {[Function]}   call     [description]
   * @param  {[Array]}   array    [description]
   * @param  {[Int]}   i        [description]
   * @param  {Function} callback [description]
   * @return {[type]}            [description]
   */
  function recursive(call, array, i, callback) {
    var args = arguments;
    call(i, function() {
      if (args[2] === array.length - 1) {
        if (callback)
          callback.apply(this, arguments);
      } else {
        args[2]++;
        recursive.apply(this, args);
      }
    });
  }

  ST.recursive = recursive;

  return ST;

}))
